package org.apache.lucene.distributed;

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.Arrays;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Crytographic utilities
 *
 */
public class Crypto {

  private static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

  public static String generateMacKey() throws Exception {
    KeyGenerator kg = KeyGenerator.getInstance("HmacMD5");
    SecretKey sk = kg.generateKey();
    byte[] bytes = sk.getEncoded();
    return encodeHex(bytes);
  }
  
  public static boolean verify(byte[] bytes, byte[] mac, String key) throws Exception {
    byte[] otherMac = mac(bytes, key);
    return Arrays.equals(otherMac, mac);
  }
  
  public static byte[] mac(byte[] bytes, String key) throws Exception {
    byte[] raw = decodeHex(key);
    SecretKeySpec skeySpec = new SecretKeySpec(raw, "HmacMD5");
    Mac mac = Mac.getInstance("HmacMD5");
    mac.init(skeySpec);
    return mac.doFinal(bytes);
  }

  public static byte[] decodeHex(String str) throws Exception {
    char[] data = str.toCharArray();
    int len = data.length;
    if ((len & 0x01) != 0) {
      throw new Exception("Odd number of characters.");
    }
    byte[] out = new byte[len >> 1];
    // two characters form the hex value.
    for (int i = 0, j = 0; j < len; i++) {
      int f = toDigit(data[j], j) << 4;
      j++;
      f = f | toDigit(data[j], j);
      j++;
      out[i] = (byte) (f & 0xFF);
    }

    return out;
  }

  protected static int toDigit(char ch, int index) throws Exception {
    int digit = Character.digit(ch, 16);
    if (digit == -1) {
      throw new Exception("Illegal hexadecimal charcter " + ch + " at index " + index);
    }
    return digit;
  }

  public static String encodeHex(byte[] data) {
    int l = data.length;
    char[] out = new char[l << 1];

    // two characters form the hex value.
    for (int i = 0, j = 0; i < l; i++) {
      out[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
      out[j++] = DIGITS[0x0F & data[i]];
    }

    return new String(out);
  }
}
